package com_.dxy_test_.synchronized_;

import com.google.common.collect.Interner;
import com.google.common.collect.Interners;

import java.util.concurrent.ConcurrentHashMap;

public class SynchronizedTest {

    private static final ConcurrentHashMap<String, Object> MAP = new ConcurrentHashMap<>();

    private static final Interner<String> pool = Interners.newWeakInterner();

    /**
     * 想象这样一个需求，在一个接口中，该接口被多个线程并发访问的，该接口主要做了以下工作，
     * 查询的时候是根据广告的类型查询符合该类型的广告，如果查询到广告，那么就返回该广告列表，
     * 但同时，由于请求量比较的大，为了增加查询速度，同时减轻数据库的负担，我们在该层加入redis ，
     * 这样，我们也许会这样做，首先，我们去redis 查询该类型的广告，如果存在那么就直接返回，
     * 如果不存在，那么我们需要 为该广告类型加锁，进行锁定，然后进行二次查询redis(二次判空，
     * 保证查询排队的线程，只要又一个查询到了，那么其他的就不用查库了)，然后查询数据库，在存入到redis 中，返回即可，
     * 其他的线程，如果当时在排队，那么在二次判空的时候就可以拿到值，其他的则在第一次查询redis 中直接拿到了值
     */

    //直接synchronized(string)，无法保证锁住的是同一个对象，因此无效
    private static Object getInfo1(String key) {
        Object value = MAP.get(key);
        if (value != null) {
            System.out.println(Thread.currentThread().getName() + "线程从***缓存***中获取到数据，value：" + value);
            return value;
        } else {
            //双重判断
            synchronized ("Lock_" + key) {
                value = MAP.get(key);
                if (value != null) {
                    System.out.println(Thread.currentThread().getName() + "线程从***缓存***中获取到数据，value：" + value);
                    return value;
                } else {
                    value = new Object();
                    System.out.println(Thread.currentThread().getName() + "线程从===数据库====中获取到数据，value：" + value);
                    MAP.put(key, value);
                    return value;
                }
            }
        }
    }

    //synchronized(string.intern())，有效
    //intern()是native方法，作用是去字符串常量池查看是否存在该字符串对象（equals() 方法进行比较）
    //如果存在，那么直接返回该对象的引用，如果不存在那么直接在常量池中创建该对象，并返回该对象的地址（该操作为JDK1.6及以前）
    private static Object getInfo2(String key) {
        Object value = MAP.get(key);
        if (value != null) {
            System.out.println(Thread.currentThread().getName() + "线程从***缓存***中获取到数据，value：" + value);
            return value;
        } else {
            //双重判断
            synchronized (("Lock_" + key).intern()) {
                value = MAP.get(key);
                if (value != null) {
                    System.out.println(Thread.currentThread().getName() + "线程从***缓存***中获取到数据，value：" + value);
                    return value;
                } else {
                    value = new Object();
                    System.out.println(Thread.currentThread().getName() + "线程从===数据库====中获取到数据，value：" + value);
                    MAP.put(key, value);
                    return value;
                }
            }
        }
    }

    //使用string.intern()，当数据量多了之后，容易印发Full GC和OOM
    //Interners内部基于ConcurrentHashMap实现，而且可以设置引用类型，不会加重full gc负担，效率更高
    private static Object getInfo3(String key) {
        Object value = MAP.get(key);
        if (value != null) {
            System.out.println(Thread.currentThread().getName() + "线程从***缓存***中获取到数据，value：" + value);
            return value;
        } else {
            //双重判断
            synchronized (pool.intern("Lock_" + key)) {
                value = MAP.get(key);
                if (value != null) {
                    System.out.println(Thread.currentThread().getName() + "线程从***缓存***中获取到数据，value：" + value);
                    return value;
                } else {
                    value = new Object();
                    System.out.println(Thread.currentThread().getName() + "线程从===数据库====中获取到数据，value：" + value);
                    MAP.put(key, value);
                    return value;
                }
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread("Thread-" + i) {
                @Override
                public void run() {
//                    getInfo1(new String("aaa"));
//                    getInfo2(new String("aaa"));
                    getInfo3(new String("aaa"));
                }
            }.start();
        }
    }
}
